Add support for GlobalSat Sport GH625XT GPS training watch
authorZingo Andersen <zingo@zingo.org>
Sun, 17 Jan 2016 15:37:56 +0000 (16:37 +0100)
committerZingo Andersen <zingo@zingo.org>
Sun, 17 Jan 2016 15:37:56 +0000 (16:37 +0100)
Makefile.in
globalsat_sport.cc [new file with mode: 0644]
reference/track/globalsat_gh625XT.bin [new file with mode: 0644]
reference/track/globalsat_gh625XT.gpx [new file with mode: 0644]
testo.d/globalsat_sport.test [new file with mode: 0644]
vecs.cc
xmldoc/formats/globalsat.xml [new file with mode: 0644]

index 87654aebfbc7d8780f3d9099719f990f84e6942a..7e8b8d2cbbb3a58b94c68b284daedd15f249fac6 100644 (file)
@@ -81,7 +81,7 @@ ALL_FMTS=$(MINIMAL_FMTS) gtm.o gpsutil.o  \
        vpl.o teletype.o jogmap.o bushnell.o bushnell_trl.o wintec_tes.o \
        subrip.o garmin_xt.o garmin_fit.o lowranceusr4.o \
        mtk_locus.o googledir.o mapbar_track.o f90g_track.o mapfactor.o energympro.o \
-       mynav.o ggv_bin.o
+       mynav.o ggv_bin.o globalsat_sport.o
 
 FMTS=@FMTS@
 
@@ -588,6 +588,9 @@ ggv_ovl.o: ggv_ovl.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h \
   grtcirc.h
 globals.o: globals.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h \
   gbfile.h cet.h inifile.h session.h src/core/datetime.h gbversion.h
+globalsat_sport.o: globalsat_sport.cc defs.h config.h queue.h zlib/zlib.h \
+ zlib/zconf.h config.h gbfile.h cet.h inifile.h session.h \
+ src/core/datetime.h gbser.h
 glogbook.o: glogbook.cc defs.h config.h queue.h zlib/zlib.h zlib/zconf.h \
   gbfile.h cet.h inifile.h session.h src/core/datetime.h xmlgeneric.h \
   src/core/file.h
diff --git a/globalsat_sport.cc b/globalsat_sport.cc
new file mode 100644 (file)
index 0000000..81d0b54
--- /dev/null
@@ -0,0 +1,879 @@
+/*
+    Global data for GPSBabel.
+
+    Copyright (C) 2012-2016, Zingo Andersen zingo@zingo.org
+    Copyright (C) 2016 Robert Lipe, robertlipe+source@gpsbabel.org
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
+
+ */
+/*
+ * This is the bridge between the GPSBabel and globalsat sport devices
+ * gh625XT. Globalsat has a few devices under the sport brand and they
+ * are using a similar USB serial protocal.
+ * Currently only gh625XT is supported by this driver but the code could
+ * extended (maybe autodetect) support more devices in the future.
+ *
+ * The code is based on GH625XT-COMMUNICATION_SPECS_20111205-1.pdf from Globasat
+ * that they nicely supplied on request.
+ *
+ * Usage examples:
+ * gpsbabel -i globalsat -f /dev/ttyUSB0 -o gpx,garminextensions -F xxx.gpx
+ * gpsbabel -i globalsat -f /dev/ttyUSB0 -o gtrnctr,course=0,sport=Running -F xxx.fit
+ *
+ */
+
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include "defs.h"
+#include "gbser.h"
+
+#define MYNAME "GlobalsatSport"
+
+static void* serial_handle;
+static int isSizeSwaped;
+
+static char* showlist = NULL;               // if true show a list instead of download tracks
+static char* track = 0;                     // if not 0 only download this track, if 0 download all
+
+static char* opt_dump_file = 0;                    // dump raw data to this file (optional)
+static char* opt_input_dump_file = NULL;    // if true input is from a dump-file instead of serial console
+static gbfile* dumpfile = NULL;             // used for creating bin/RAW datadump files, usefull for testing
+static gbfile* in_file = NULL;              // used for reading from bin/RAW datadump files, usefull for testing
+
+static
+arglist_t globalsat_args[] = {
+  {"showlist", &showlist, "list tracks", NULL, ARGTYPE_BOOL, ARG_NOMINMAX},
+  {"track", &track, "get track", "0", ARGTYPE_INT, ARG_NOMINMAX},
+  {"dump-file", &opt_dump_file, "Dump raw data to this file", NULL, ARGTYPE_OUTFILE, ARG_NOMINMAX},
+  {"input-is-dump-file", &opt_input_dump_file, "Dump raw data to this file", NULL, ARGTYPE_BOOL, ARG_NOMINMAX},
+  ARG_TERMINATOR
+};
+
+typedef enum {
+  CommandWhoAmI = 0xBF,
+  CommandGetSystemInformation = 0x85,
+  CommandGetSystemConfiguration = 0x86,
+
+  CommandSetSystemConfiguration = 0x96,
+  CommandSetSystemInformation = 0x98,
+  CommandGetScreenshot = 0x83,
+
+  CommandGetWaypoints = 0x77,
+  CommandSendWaypoint = 0x76,
+  CommandDeleteWaypoints = 0x75,
+
+  CommandSendRoute = 0x93,
+  CommandDeleteAllRoutes = 0x97,
+
+  CommandGetTrackFileHeaders = 0x78,
+  CommandGetTrackFileSections = 0x80,
+  CommandGetNextTrackSection = 0x81,
+  //CommandReGetLastSection = 0x82,
+  CommandId_FINISH = 0x8A,
+  CommandSendTrackStart = 0x90,
+  CommandSendTrackSection = 0x91,
+
+  HeaderTypeLaps = 0xAA,
+  HeaderTypeTrackPoints = 0x55,
+
+  ResponseInsuficientMemory = 0x95,
+  ResponseResendTrackSection = 0x92,
+  ResponseSendTrackFinish = 0x9A
+} globalsat_commands_e;
+
+typedef struct tagDATE {
+  uint8_t Year;
+  uint8_t Month;
+  uint8_t Day;
+} gh_date;
+
+typedef struct tagTIME {
+  uint8_t Hour;
+  uint8_t Minute;
+  uint8_t Second;
+} gh_time;
+
+typedef struct tagTRAINHEADER {
+  gh_date dateStart;
+  gh_time timeStart;
+  uint32_t TotalPoint;         //6-9
+  uint32_t TotalTime;          //10-13
+  uint32_t TotalDistance;      //14-17
+  uint16_t LapCnts;            //18-19
+  union {
+    uint32_t Index;
+    uint32_t StartPt;
+  } gh_ptrec;                  //20-23
+  union {
+    uint32_t LapIndex;
+    uint32_t EndPt;
+  } gh_laprec;                 //24-27
+  uint8_t DataType;            //28
+} gh_trainheader;
+
+typedef struct tagDB_TRAIN {
+  gh_date dateStart;
+  gh_time timeStart;
+  uint32_t TotalPoint;         //6-9
+  uint32_t TotalTime;          //10-13
+  uint32_t TotalDistance;      //14-17
+  uint16_t LapCnts;            //18-19
+  union {
+    uint32_t Index;
+    uint32_t StartPt;
+  } gh_ptrec;                  //20-23
+  union {
+    uint32_t LapIndex;
+    uint32_t EndPt;
+  } gh_laprec;                 //24-27
+  uint8_t MultiSport;          //28 on/off
+  uint16_t Calory;             //29-30
+  uint32_t MaxSpeed;
+  uint8_t MaxHeart;
+  uint8_t AvgHeart;
+
+  uint16_t Ascent;
+  uint16_t Descent;
+
+  int16_t MinAlti;
+  int16_t MaxAlti;
+  uint16_t AvgCadns;
+  uint16_t BestCadns;
+  uint16_t AvgPower;
+  uint16_t MaxPower;
+  uint8_t Sport1;
+  uint8_t Sport2;
+  uint8_t Sport3;
+  uint8_t Sport4;
+  uint8_t Sport5;
+} gh_db_train;
+
+typedef struct tagDB_LAP {
+  uint32_t AccruedTime;
+  uint32_t TotalTime;
+  uint32_t TotalDistance;
+  uint16_t Calory;
+  uint32_t MaxSpeed;
+  uint8_t MaxHeart;
+  uint8_t AvgHeart;
+  int16_t MinAlti;
+  int16_t MaxAlti;
+  uint16_t AvgCadns;
+  uint16_t BestCadns;
+  uint16_t AvgPower;
+  uint16_t MaxPower;
+  uint8_t MultiSportIndex;
+  uint32_t StartPt;
+  uint32_t EndPt;
+} gh_db_lap;
+
+typedef struct tagRECPOINT {
+  uint32_t Latitude;
+  uint32_t Longitude;
+  int16_t Altitude;
+  uint32_t Speed;
+  uint8_t HeartRate;
+  uint32_t IntervalTime;
+  uint16_t Cadence;
+  uint16_t PwrCadence;
+  uint16_t Power;
+} gh_recpoint;
+
+static void
+serial_init(const char* fname)
+{
+  if (serial_handle = gbser_init(fname), NULL == serial_handle) {
+    fatal(MYNAME ": Can't open port '%s'\n", fname);
+  }
+  if (gbser_set_speed(serial_handle, 115200) != gbser_OK) {
+    fatal(MYNAME ": Can't configure port '%s'\n", fname);
+  }
+  // Toss anything that came in before our speed was set
+  gbser_flush(serial_handle);
+}
+
+static void
+serial_deinit(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " serial_deinit()\n");
+  }
+  gbser_deinit(serial_handle);
+  serial_handle = NULL;
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " serial_deinit() Done\n");
+  }
+}
+
+static int
+serial_recv_byte()
+{
+  int result;
+  /* allow for a delay of 4s */
+  result = gbser_readc_wait(serial_handle, 4000);
+  switch (result) {
+  case gbser_ERROR:
+    fatal("serial_recv_byte(): error reading one byte\n");
+    break;
+  case gbser_NOTHING:
+    fatal("serial_recv_byte(): read timeout\n");
+    break;
+  }
+  return result;
+}
+
+static void
+serial_write_byte(uint8_t byte)
+{
+  int n;
+  if (global_opts.debug_level > 1) {
+    printf("0x%02x (%d), ", byte, byte);
+  }
+
+  n = gbser_writec(serial_handle, byte);
+  if (n == gbser_ERROR) {
+    fatal("globalsat_probe_device(): write failed\n");
+  }
+}
+
+static int
+recv_byte()
+{
+  int result=0;
+  // Read from serial or dumpfile
+  if (!opt_input_dump_file) {
+    result=serial_recv_byte();
+  } else {
+    int bytes;
+    bytes=gbfread(&result, 1, 1, in_file);
+    is_fatal((bytes != 1), MYNAME ": read error");
+  }
+  // Check if byte sould be dumped also into a file
+  if (dumpfile) {
+    gbfwrite(&result, 1, 1, dumpfile);
+  }
+  return result;
+}
+
+static void
+write_byte(uint8_t byte)
+{
+  // Write serial or not at all if input is a dumpfile
+  if (!opt_input_dump_file) {
+    serial_write_byte(byte);
+  }
+  //else {
+  // Do nothing as commands are not used when dumpfile is used instead
+  // of serial device
+  //}
+}
+
+
+static void
+globalsat_write_package(uint8_t* payload, uint32_t size)
+{
+  //All globalsat devices but gh561
+  //2 <len_h> <len_l> <payload...> <crc>
+  // gh561 (isSizeSwaped)
+  //2 <len_l> <len_h> <payload...> <crc>
+
+  uint32_t i;
+
+  uint8_t crc = 0;
+  write_byte(2);
+
+  if (!isSizeSwaped) {
+    write_byte((0xff00 & size) >> 8);
+    crc ^= (0xff00 & size) >> 8;
+    write_byte(0xff & size);
+    crc ^= (0xff & size);
+  } else {
+    write_byte(0xff & size);
+    crc ^= (0xff & size);
+    write_byte((0xff00 & size) >> 8);
+    crc ^= (0xff00 & size) >> 8;
+  }
+
+  if (payload != NULL) {
+    for (i = 0; i < size; i++) {
+      write_byte(payload[i]);
+      crc ^= payload[i];
+    }
+  }
+  write_byte(crc);
+  if (global_opts.debug_level > 1) {
+    printf("\n");
+  }
+}
+
+static uint8_t*
+globalsat_read_package(int* out_length, uint8_t* out_DeviceCommand)
+{
+  uint8_t DeviceCommand, len_h, len_l, crc;
+  uint8_t* payload;
+  int length;
+  int i;
+  uint8_t calc_crc = 0;
+
+  DeviceCommand = recv_byte();
+  if (global_opts.debug_level > 1) {
+    printf("DeviceCommand: 0x%02x ", DeviceCommand);
+  }
+  len_h = recv_byte();
+  calc_crc ^= len_h;
+  len_l = recv_byte();
+  calc_crc ^= len_l;
+
+  length = (len_h << 8) + len_l;
+  if (global_opts.debug_level > 1) {
+    printf("len=%d Payload:", length);
+  }
+
+  payload = (uint8_t*) malloc(length);
+  if (payload == NULL) {
+    goto error_out;
+  }
+
+  for (i = 0; i < length; i++) {
+    payload[i] = recv_byte();
+    calc_crc ^= payload[i];
+  }
+
+  crc = recv_byte();
+  if (global_opts.debug_level > 1) {
+    printf("crc=0x%x should be=0x%x\n", crc, calc_crc);
+  }
+  if (crc == calc_crc) {
+    *out_DeviceCommand = DeviceCommand;
+    *out_length = length;
+    return payload;
+  }
+  //crc error
+  free(payload);
+error_out:
+  *out_length = 0;
+  return NULL;
+}
+
+/*
+ * Send one byte package
+ */
+static void
+globalsat_send_simple(uint8_t command)
+{
+  uint8_t payload[1];
+  payload[0] = command;
+  globalsat_write_package(payload, 1);
+}
+
+static void
+globalsat_probe_device()
+{
+  //TODO try this first if fails try with false, to support 561
+  isSizeSwaped = FALSE;                //all devices but gh561 since gh561 has swaped size.
+
+  globalsat_send_simple(CommandWhoAmI);
+
+  uint8_t* payload;
+  int len;
+  uint8_t DeviceCommand;
+  payload = globalsat_read_package(&len, &DeviceCommand);
+  if ((len > 0) && (payload != NULL)) {
+    if (global_opts.debug_level > 1) {
+      printf("Got package!!!\n");
+    }
+    //TODO figure out what device it is if we start to support more devices then gh625XT
+  }
+
+  if (payload) {
+    free(payload);
+  }
+}
+
+static void
+rd_init(const char* fname)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " rd_init()\n");
+  }
+  if (opt_dump_file) {
+    dumpfile = gbfopen(opt_dump_file, "w", MYNAME);
+    if (!dumpfile) {
+      printf(MYNAME " rd_init() creating dumpfile %s FAILED continue anyway\n",opt_dump_file);
+    } else {
+      if (global_opts.debug_level > 1) {
+        printf(MYNAME " rd_init() creating dumpfile %s for writing binnary copy of serial stream\n",opt_dump_file);
+      }
+    }
+  }
+  if (!opt_input_dump_file) {
+    serial_init(fname);
+  } else {
+    // read from dump-file instead of serial
+    in_file = gbfopen(fname, "r", MYNAME);
+    is_fatal(!in_file, "Could not open dumpfile for input: %s", fname);
+
+  }
+  globalsat_probe_device();
+}
+
+static void
+wr_init(const char* fname)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " wr_init()\n");
+  }
+  serial_init(fname);
+}
+
+
+static void
+rd_deinit(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " rd_deinit()\n");
+  }
+  if (!opt_input_dump_file) {
+    serial_deinit();
+  } else {
+    if (in_file) {
+      gbfclose(in_file);
+    }
+  }
+  if (dumpfile) {
+    gbfclose(dumpfile);
+    dumpfile = NULL;
+  }
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " rd_deinit() Done\n");
+  }
+}
+
+static void
+wr_deinit(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " wr_deinit()\n");
+  }
+  serial_deinit();
+}
+
+static void track_read(void);
+
+
+static void
+waypoint_read(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME "   waypoint_read()\n");
+  }
+  //CommandGetTrackFileHeaders
+  globalsat_send_simple(CommandGetWaypoints);
+
+  uint8_t* in_payload;
+  int len;
+  uint8_t DeviceCommand;
+  in_payload = globalsat_read_package(&len, &DeviceCommand);
+  if ((len > 0) && (in_payload != NULL)) {
+    if (global_opts.debug_level > 1) {
+      printf("Got package!!!\n");
+    }
+  }
+  if (in_payload) {
+    free(in_payload);
+  }
+  track_read();
+}
+
+static void
+track_read(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME "   track_read()\n");
+  }
+  //CommandGetTrackFileHeaders
+  globalsat_send_simple(CommandGetTrackFileHeaders);
+  if (global_opts.debug_level > 1) {
+    printf("Sent...\n");
+  }
+
+  uint8_t* payload;
+  int length;
+  uint8_t DeviceCommand;
+  payload = globalsat_read_package(&length, &DeviceCommand);
+  if ((length > 0) && (payload != NULL)) {
+    if (global_opts.debug_level > 1) {
+      printf("Got package!!! headers\n");
+    }
+    int number_headers;
+    //payload is packed with a number of trainingheaders with the size of 29bytes each
+    number_headers = length / 29;      //29=packed sizeof(gh_trainheader)
+    if (global_opts.debug_level > 1) {
+      printf("length=%d sizeof(gh_trainheader)=%d number_headers=%d\n", (int) length, (int) 29, (int) number_headers);
+    }
+
+    for (int i = 0; i < number_headers; i++) {
+      int pos = i * 29; //29=packed sizeof(gh_trainheader)
+      uint8_t* hdr = (uint8_t*) & (payload[pos]);
+      gh_trainheader header;
+      header.dateStart.Year = hdr[0];
+      header.dateStart.Month = hdr[1];
+      header.dateStart.Day = hdr[2];
+      header.timeStart.Hour = hdr[3];
+      header.timeStart.Minute = hdr[4];
+      header.timeStart.Second = hdr[5];
+      header.TotalPoint = be_read32(hdr+6);
+      header.TotalTime = be_read32(hdr+10);
+      header.TotalDistance = be_read32(hdr+14);
+      header.LapCnts = be_read16(hdr+18);
+      header.gh_ptrec.Index = be_read32(hdr+20);
+      header.gh_laprec.LapIndex = be_read32(hdr+24);
+      header.DataType = hdr[28];
+
+      if (showlist || global_opts.debug_level > 1) {
+        printf("Track[%02i]: %02d-%02d-%02d ", i,header.dateStart.Year,header.dateStart.Month, header.dateStart.Day);
+        printf("%02d:%02d:%02d ", header.timeStart.Hour,header.timeStart.Minute, header.timeStart.Second);
+        int time_s=header.TotalTime / 10;
+        int time_h=time_s/(60*60);
+        time_s-=time_h*(60*60);
+        int time_m=time_s/60;
+        time_s-=time_m*60;
+        printf("Points:%6d Time:%02d:%02d:%02d Dist:%9dm LapCnts:%5d ",        header.TotalPoint, time_h,time_m,time_s, header.TotalDistance, header.LapCnts);
+        printf("Index/StartPt:%d ", header.gh_ptrec.Index);
+        printf("LapIndex/EndPt:%d ", header.gh_laprec.LapIndex);
+        printf("DataType:0x%x\n", header.DataType);
+      }
+
+      if (!showlist) {
+        route_head* trk = route_head_alloc();
+
+        QString str;
+        str.sprintf("%02d-%02d-%02d_%02d:%02d:%02d", header.dateStart.Year, header.dateStart.Month, header.dateStart.Day, header.timeStart.Hour, header.timeStart.Minute, header.timeStart.Second);
+        trk->rte_name = str;
+        trk->rte_desc = QString("GH625XT GPS tracklog data");
+
+        track_add_head(trk);
+
+        uint8_t GetTrack[5];
+        GetTrack[0] = CommandGetTrackFileSections;
+        GetTrack[1] = 0x0;
+        GetTrack[2] = 0x1;
+        GetTrack[3] = (0xFF00 & header.gh_ptrec.Index) >> 8;
+        GetTrack[4] = 0xFF & header.gh_ptrec.Index;
+        globalsat_write_package(GetTrack, 5);
+
+        uint8_t trackDeviceCommand;
+        int track_length;
+        uint8_t* track_payload = NULL;
+        track_payload = globalsat_read_package(&track_length, &trackDeviceCommand);
+        is_fatal(((track_length == 0) || (track_payload == NULL)) , "tracklength in 0 bytes or payload nonexistant");
+        //      printf("Got track package!!! Train data\n");
+
+        uint8_t* dbtrain = (uint8_t*) track_payload;
+        gh_db_train db_train;
+        db_train.dateStart.Year = dbtrain[0];
+        db_train.dateStart.Month = dbtrain[1];
+        db_train.dateStart.Day = dbtrain[2];
+        db_train.timeStart.Hour = dbtrain[3];
+        db_train.timeStart.Minute = dbtrain[4];
+        db_train.timeStart.Second = dbtrain[5];
+        db_train.TotalPoint = be_read32(dbtrain+6);
+        db_train.TotalTime = be_read32(dbtrain+10);
+        db_train.TotalDistance = be_read32(dbtrain+14);
+        db_train.LapCnts = be_read16(dbtrain+18);
+        db_train.gh_ptrec.Index = be_read32(dbtrain+20);
+        db_train.gh_laprec.LapIndex = be_read32(dbtrain+24);
+        db_train.MultiSport = dbtrain[28];
+        db_train.Calory = be_readu16(dbtrain+29);
+        db_train.MaxSpeed = be_read32(dbtrain+31);
+        db_train.MaxHeart = dbtrain[35];
+        db_train.AvgHeart = dbtrain[36];
+        db_train.Ascent = be_readu16(dbtrain+37);
+        db_train.Descent = be_readu16(dbtrain+39);
+        db_train.MinAlti = be_read16(dbtrain+41);
+        db_train.MaxAlti = be_read16(dbtrain+43);
+        db_train.AvgCadns = be_readu16(dbtrain+45);
+        db_train.BestCadns = be_readu16(dbtrain+47);
+        db_train.AvgPower = be_readu16(dbtrain+49);
+        db_train.MaxPower = be_readu16(dbtrain+51);
+        db_train.Sport1 = dbtrain[53];
+        db_train.Sport2 = dbtrain[54];
+        db_train.Sport3 = dbtrain[55];
+        db_train.Sport4 = dbtrain[56];
+        db_train.Sport5 = dbtrain[57];
+
+        if (global_opts.debug_level > 1) {
+          printf("\nTrainData:%02d-%02d-%02d ", db_train.dateStart.Year,db_train.dateStart.Month, db_train.dateStart.Day);
+          printf("%02d:%02d:%02d ", db_train.timeStart.Hour, db_train.timeStart.Minute, db_train.timeStart.Second);
+          printf("Total(points:%6d time:%6ds dist:%9dm) LapCnts:%5d ", db_train.TotalPoint,db_train.TotalTime / 10,db_train.TotalDistance, db_train.LapCnts);
+          printf("Index/StartPt:%d ", db_train.gh_ptrec.Index);
+          printf("LapIndex/EndPt:%d ", db_train.gh_laprec.LapIndex);
+          printf("MultiSport:0x%x ", db_train.MultiSport);
+        }
+        int total_laps = db_train.LapCnts;
+        int total_laps_left = total_laps;
+        free(track_payload);
+        track_payload = NULL;
+
+        gpsbabel::DateTime gpsDateTime;
+
+        // Get laps
+        while (total_laps_left > 0) {
+          globalsat_send_simple(CommandGetNextTrackSection);
+          track_payload = globalsat_read_package(&track_length, &trackDeviceCommand);
+          is_fatal(((track_length == 0) || (track_payload == NULL)), "tracklength in 0 bytes or payload nonexistant");
+          //   printf("Got track package!!! Laps data\n");
+
+          uint8_t* hdr = (uint8_t*) track_payload;
+          gh_trainheader header;
+          header.dateStart.Year = hdr[0];
+          header.dateStart.Month = hdr[1];
+          header.dateStart.Day = hdr[2];
+          header.timeStart.Hour = hdr[3];
+          header.timeStart.Minute = hdr[4];
+          header.timeStart.Second = hdr[5];
+          header.TotalPoint = be_read32(hdr+6);
+          header.TotalTime = be_read32(hdr+10);
+          header.TotalDistance = be_read32(hdr+14);
+          header.LapCnts = be_read16(hdr+18);
+          header.gh_ptrec.Index = be_read32(hdr+20);
+          header.gh_laprec.LapIndex = be_read32(hdr+24);
+          header.DataType = hdr[28];
+
+
+          if (global_opts.debug_level > 1) {
+            printf("Lap Trainheader: %02d-%02d-%02d ", header.dateStart.Year, header.dateStart.Month, header.dateStart.Day);
+            printf("%02d:%02d:%02d ", header.timeStart.Hour, header.timeStart.Minute, header.timeStart.Second);
+            printf("Total(points:%6d time:%6ds dist:%9dm) LapCnts:%5d ", header.TotalPoint,header.TotalTime / 10, header.TotalDistance, header.LapCnts);
+            printf("Index/StartPt:%d ", header.gh_ptrec.Index);
+            printf("LapIndex/EndPt:%d ", header.gh_laprec.LapIndex);
+            printf("DataType:0x%x\n", header.DataType);
+          }
+
+          /*
+           * GPS year: 2000+; struct tm year: 1900+
+           * GPS month: 1-12, struct tm month: 0-11
+           */
+
+          QDate gpsDate = QDate(header.dateStart.Year+2000,header.dateStart.Month,header.dateStart.Day);
+          QTime gpsTime = QTime(header.timeStart.Hour-2,header.timeStart.Minute,header.timeStart.Second);
+          gpsDateTime = gpsbabel::DateTime(gpsDate,gpsTime);
+          gpsDateTime.setTimeSpec(Qt::UTC);
+
+          int laps_in_package = header.gh_laprec.LapIndex - header.gh_ptrec.Index + 1;
+//                                     printf("Lap Data:\n");
+          uint8_t* lap_start_pos = track_payload + 29; //29=packed sizeof(gh_trainheader)
+          int lap;
+          for (lap = 0; lap < laps_in_package; lap++) {
+            uint8_t* dblap = (uint8_t*)(lap_start_pos) + lap * 41;     //  packed sizeof(gh_db_lap)=41
+            gh_db_lap db_lap;
+
+            db_lap.AccruedTime = be_read32(dblap+0);
+            db_lap.TotalTime = be_read32(dblap+4);
+            db_lap.TotalDistance = be_read32(dblap+8);
+            db_lap.Calory = be_readu16(dblap+12);
+            db_lap.MaxSpeed = be_read32(dblap+14);
+            db_lap.MaxHeart = dblap[18];
+            db_lap.AvgHeart = dblap[19];
+            db_lap.MinAlti = be_read16(dblap+20);
+            db_lap.MaxAlti = be_read16(dblap+22);
+            db_lap.AvgCadns = be_readu16(dblap+24);
+            db_lap.BestCadns = be_readu16(dblap+26);
+            db_lap.AvgPower = be_readu16(dblap+28);
+            db_lap.MaxPower = be_readu16(dblap+30);
+            db_lap.MultiSportIndex = dblap[32];
+            db_lap.StartPt = be_read32(dblap+33);
+            db_lap.EndPt = be_read32(dblap+37);
+
+            if (global_opts.debug_level > 1) {
+              printf("     lap[%d] AccruedTime:%ds TotalTime:%ds TotalDist:%dm", lap, db_lap.AccruedTime, db_lap.TotalTime / 10, db_lap.TotalDistance);
+              printf(" Calory:%d MaxSpeed:%d Hearth max:%d avg:%d ", db_lap.Calory, db_lap.MaxSpeed, db_lap.MaxHeart, db_lap.AvgHeart);
+              printf(" Alt min:%d max:%d", db_lap.MinAlti, db_lap.MaxAlti);
+              printf(" Cadns avg:%d best:%d", db_lap.AvgCadns, db_lap.BestCadns);
+              printf(" Power avg:%d Max:%d", db_lap.AvgPower, db_lap.MaxPower);
+              printf(" MultisportIndex:%d", db_lap.MultiSportIndex);
+              printf(" StartPt:%d EndPt:%d\n", db_lap.StartPt, db_lap.EndPt);
+            }
+          }
+          free(track_payload);
+          track_payload = NULL;
+
+          total_laps_left -= laps_in_package;
+        }
+
+        globalsat_send_simple(CommandGetNextTrackSection);
+        do {
+          if (track_payload) {
+            // rest of the time in the loop
+            free(track_payload);
+            globalsat_send_simple(CommandGetNextTrackSection);
+          }
+
+          track_payload = globalsat_read_package(&track_length, &trackDeviceCommand);
+          if ((track_length > 0) && (track_payload != NULL)) {
+            //   printf("Got track package!!! Train data\n");
+            uint8_t* hdr = (uint8_t*) track_payload;
+            gh_trainheader header;
+            header.dateStart.Year = hdr[0];
+            header.dateStart.Month = hdr[1];
+            header.dateStart.Day = hdr[2];
+            header.timeStart.Hour = hdr[3];
+            header.timeStart.Minute = hdr[4];
+            header.timeStart.Second = hdr[5];
+            header.TotalPoint = be_read32(hdr+6);
+            header.TotalTime = be_read32(hdr+10);
+            header.TotalDistance = be_read32(hdr+14);
+            header.LapCnts = be_read16(hdr+18);
+            header.gh_ptrec.StartPt = be_read32(hdr+20);
+            header.gh_laprec.EndPt = be_read32(hdr+24);
+            header.DataType = hdr[28];
+
+
+            if (global_opts.debug_level > 1) {
+              printf("Lap Trainheader: %02d-%02d-%02d ", header.dateStart.Year, header.dateStart.Month, header.dateStart.Day);
+              printf("%02d:%02d:%02d ", header.timeStart.Hour, header.timeStart.Minute, header.timeStart.Second);
+              printf("Total(points:%6d time:%6ds dist:%9dm) LapCnts:%5d ", header.TotalPoint, header.TotalTime / 10, header.TotalDistance, header.LapCnts);
+              printf("StartPt:%d ", header.gh_ptrec.StartPt);
+              printf("EndPt:%d ", header.gh_laprec.EndPt);
+              printf("DataType:0x%x\n", header.DataType);
+            }
+
+            int recpoints_in_package = header.gh_laprec.EndPt - header.gh_ptrec.StartPt + 1;
+            //   printf("Recpoints Data:\n");
+            uint8_t* recpoints_start_pos = track_payload + 29; //29=packed sizeof(gh_trainheader)
+            int recpoint;
+            for (recpoint = 0; recpoint < recpoints_in_package; recpoint++) {
+              uint8_t* ghpoint = (uint8_t*)(recpoints_start_pos + recpoint * 25);      //  packed sizeof(gh_recpoint)=25
+              gh_recpoint point;
+              point.Latitude = be_read32(ghpoint);
+              point.Longitude = be_read32(ghpoint+4);
+              point.Altitude = be_read16(ghpoint+8);
+              point.Speed = be_read32(ghpoint+10);
+              point.HeartRate = ghpoint[14];
+              point.IntervalTime = be_read32(ghpoint+15);
+              point.Cadence = be_readu16(ghpoint+19);
+              point.PwrCadence = be_readu16(ghpoint+21);
+              point.Power = be_readu16(ghpoint+23);
+
+              //Time from last point in sec's * 10 (e.g. point.lntervalTime is sec multiplied witn 10)
+              // convert to milisecs
+              gpsbabel::DateTime gpsbabeltime = gpsDateTime.addMSecs(point.IntervalTime*100);
+              gpsbabeltime.setTimeSpec(Qt::UTC);
+              gpsDateTime.setDate(gpsbabeltime.date());
+              gpsDateTime.setTime(gpsbabeltime.time());
+
+              // if (global_opts.debug_level > 1) {
+              //   qDebug() << "DateTime2:" << gpsDateTime.toString();
+              // }
+              if (global_opts.debug_level > 1) {
+                printf("     recpoint[%2d] Lat:%f Long:%f Alt:%dm", recpoint,(double)((int32_t) point.Latitude) / 1000000.0,(double)((int32_t) point.Longitude) / 1000000.0, point.Altitude);
+                printf(" Speed:%f HR:%d",(double) point.Speed / 100, point.HeartRate);
+                printf(" Time:%d Cadence:%d", point.IntervalTime, point.Cadence);
+                printf(" PwrCadense:%d Power:%d\n", point.PwrCadence,point.Power);
+              }
+
+              Waypoint* wpt = new Waypoint(); // waypt_new();
+              //wpt->creation_time = mkgmtime(&gpstime);
+              wpt->SetCreationTime(gpsbabeltime);
+              wpt->longitude = ((int32_t) point.Longitude) / 1000000.0;
+              wpt->latitude = ((int32_t) point.Latitude) / 1000000.0;
+              wpt->altitude = point.Altitude;
+              wpt->speed = ((double) point.Speed / 100.0) * 1000.0 / 3600.0;
+              wpt->heartrate = point.HeartRate;
+              wpt->cadence = point.Cadence;    //TODO convert in any way??
+              wpt->power = point.Power;        //TODO convert in any way??
+
+              track_add_wpt(trk, wpt);
+            }
+          }
+        } while (trackDeviceCommand == CommandGetTrackFileSections);
+        if (track_payload) {
+          free(track_payload);
+        }
+      }
+    }
+  }
+  if (payload) {
+    free(payload);
+  }
+}
+
+static void
+route_read(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME "   route_read() TODO\n");
+  }
+}
+
+static void
+data_read(void)
+{
+  if (global_opts.debug_level > 1) {
+    printf(MYNAME " data_read()\n");
+  }
+
+  if (global_opts.masked_objective & WPTDATAMASK) {
+    waypoint_read();
+  }
+  if (global_opts.masked_objective & TRKDATAMASK) {
+    track_read();
+  }
+  if (global_opts.masked_objective & RTEDATAMASK) {
+    route_read();
+  }
+  if (!(global_opts.masked_objective &
+        (WPTDATAMASK | TRKDATAMASK | RTEDATAMASK | POSNDATAMASK))) {
+    fatal(MYNAME ": Nothing to do.\n");
+  }
+}
+
+// This used the serial comunication to the watch
+ff_vecs_t globalsat_sport_vecs = {
+  ff_type_serial,              //type
+  FF_CAP_RW_ALL,               //cap[3]
+  rd_init,                     //rd_init
+  wr_init,                     //wr_init
+  rd_deinit,                   //rd_deinit
+  wr_deinit,                   //wr_deinit
+  data_read,                   //read
+  NULL,                        //write
+  NULL,                                //exit
+  globalsat_args,              //args
+  CET_CHARSET_ASCII, 0         //encode,fixed_encode
+  //NULL                   //name dynamic/internal?
+};
+
+// This reads from a RAW dump bile from a watch
+// Usefull for testing generata a dumpfile with
+// gpsbabel -i globalsat,dump-file=<dumpfilename> -f /dev/ttyUSB0 -o gpx,garminextensions -F <1:st gpx file name>
+// gpsbabel -i globalsat-bin -f <dumpfilename> -o gpx,garminextensions -F <2:nd gpx file name>
+ff_vecs_t globalsat_sport_fvecs = {
+  ff_type_serial,              //type
+  FF_CAP_RW_ALL,               //cap[3]
+  rd_init,                     //rd_init
+  wr_init,                     //wr_init
+  rd_deinit,                   //rd_deinit
+  wr_deinit,                   //wr_deinit
+  data_read,                   //read
+  NULL,                        //write
+  NULL,                                //exit
+  globalsat_args,              //args
+  CET_CHARSET_ASCII, 0         //encode,fixed_encode
+  //NULL                   //name dynamic/internal?
+};
diff --git a/reference/track/globalsat_gh625XT.bin b/reference/track/globalsat_gh625XT.bin
new file mode 100644 (file)
index 0000000..32e79cc
Binary files /dev/null and b/reference/track/globalsat_gh625XT.bin differ
diff --git a/reference/track/globalsat_gh625XT.gpx b/reference/track/globalsat_gh625XT.gpx
new file mode 100644 (file)
index 0000000..7a389bc
--- /dev/null
@@ -0,0 +1,730 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gpx version="1.1" creator="GPSBabel - http://www.gpsbabel.org" xmlns="http://www.topografix.com/GPX/1/1" xmlns:gpxx="http://www.garmin.com/xmlschemas/GpxExtensions/v3" xmlns:gpxtpx="http://www.garmin.com/xmlschemas/TrackPointExtension/v1">
+  <metadata>
+    <time>1970-01-01T00:00:00Z</time>
+    <bounds minlat="55.718983000" minlon="13.178168000" maxlat="55.730510000" maxlon="13.194072000"/>
+  </metadata>
+  <trk>
+    <name>16-01-15_21:58:59</name>
+    <desc>GH625XT GPS tracklog data</desc>
+    <trkseg>
+      <trkpt lat="55.718995000" lon="13.194068000">
+        <ele>77.000000</ele>
+        <time>2016-01-15T19:58:59Z</time>
+      </trkpt>
+      <trkpt lat="55.718983000" lon="13.194072000">
+        <ele>77.000000</ele>
+        <time>2016-01-15T19:59:07.100Z</time>
+      </trkpt>
+      <trkpt lat="55.718995000" lon="13.193953000">
+        <ele>79.000000</ele>
+        <time>2016-01-15T19:59:13Z</time>
+      </trkpt>
+      <trkpt lat="55.719077000" lon="13.193882000">
+        <ele>76.000000</ele>
+        <time>2016-01-15T19:59:18.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719210000" lon="13.193727000">
+        <ele>74.000000</ele>
+        <time>2016-01-15T19:59:23.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719335000" lon="13.193510000">
+        <ele>68.000000</ele>
+        <time>2016-01-15T19:59:29.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719378000" lon="13.193293000">
+        <ele>65.000000</ele>
+        <time>2016-01-15T19:59:34.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719383000" lon="13.193087000">
+        <ele>62.000000</ele>
+        <time>2016-01-15T19:59:39.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719388000" lon="13.192798000">
+        <ele>60.000000</ele>
+        <time>2016-01-15T19:59:45.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719397000" lon="13.192617000">
+        <ele>58.000000</ele>
+        <time>2016-01-15T19:59:50.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719417000" lon="13.192395000">
+        <ele>56.000000</ele>
+        <time>2016-01-15T19:59:55.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719485000" lon="13.192252000">
+        <ele>55.000000</ele>
+        <time>2016-01-15T20:00:01.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719563000" lon="13.191995000">
+        <ele>54.000000</ele>
+        <time>2016-01-15T20:00:07.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719565000" lon="13.191782000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:00:12.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719548000" lon="13.191552000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:00:17.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719560000" lon="13.191320000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:00:22.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719565000" lon="13.191082000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:00:27.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719550000" lon="13.190835000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:00:32.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719487000" lon="13.190633000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:00:37.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719413000" lon="13.190442000">
+        <ele>54.000000</ele>
+        <time>2016-01-15T20:00:42.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719327000" lon="13.190257000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:00:47.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719277000" lon="13.190047000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:00:52.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719205000" lon="13.189870000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:00:57.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719153000" lon="13.189675000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:01:02.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719235000" lon="13.189478000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:01:07.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719315000" lon="13.189307000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:01:12.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719418000" lon="13.189418000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:01:17.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719537000" lon="13.189540000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:01:23.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719592000" lon="13.189250000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:01:28.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719610000" lon="13.188938000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:01:34Z</time>
+      </trkpt>
+      <trkpt lat="55.719632000" lon="13.188697000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:01:39.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719648000" lon="13.188465000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:01:44.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719655000" lon="13.188270000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:01:49.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719728000" lon="13.188345000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:01:54.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719825000" lon="13.188527000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:01:59.100Z</time>
+      </trkpt>
+      <trkpt lat="55.719923000" lon="13.188677000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:02:04.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720023000" lon="13.188832000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:02:09.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720145000" lon="13.188975000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:02:15.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720258000" lon="13.189060000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:02:20.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720377000" lon="13.188952000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:02:25.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720487000" lon="13.188858000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:02:30.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720583000" lon="13.188745000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:02:35.200Z</time>
+      </trkpt>
+      <trkpt lat="55.720713000" lon="13.188642000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:02:41.100Z</time>
+      </trkpt>
+      <trkpt lat="55.720910000" lon="13.188607000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:02:55Z</time>
+      </trkpt>
+      <trkpt lat="55.721063000" lon="13.188368000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:03:00Z</time>
+      </trkpt>
+      <trkpt lat="55.721187000" lon="13.188262000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:03:05.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721290000" lon="13.188130000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:03:10.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721372000" lon="13.188002000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:03:15.200Z</time>
+      </trkpt>
+      <trkpt lat="55.721462000" lon="13.187827000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:03:21.200Z</time>
+      </trkpt>
+      <trkpt lat="55.721540000" lon="13.187650000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:03:27.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721633000" lon="13.187552000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:03:32.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721755000" lon="13.187567000">
+        <ele>54.000000</ele>
+        <time>2016-01-15T20:03:38.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721847000" lon="13.187658000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:03:43.100Z</time>
+      </trkpt>
+      <trkpt lat="55.721962000" lon="13.187733000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:03:48.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722102000" lon="13.187792000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:03:53.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722222000" lon="13.187870000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:03:58.200Z</time>
+      </trkpt>
+      <trkpt lat="55.722352000" lon="13.187923000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:04:04.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722455000" lon="13.187967000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:04:09.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722573000" lon="13.187998000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:04:14.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722718000" lon="13.188008000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:04:19.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722850000" lon="13.188025000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:04:24.100Z</time>
+      </trkpt>
+      <trkpt lat="55.722953000" lon="13.188067000">
+        <ele>51.000000</ele>
+        <time>2016-01-15T20:04:29.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723097000" lon="13.188127000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:04:35.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723213000" lon="13.188210000">
+        <ele>52.000000</ele>
+        <time>2016-01-15T20:04:40.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723322000" lon="13.188342000">
+        <ele>54.000000</ele>
+        <time>2016-01-15T20:04:45.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723457000" lon="13.188488000">
+        <ele>56.000000</ele>
+        <time>2016-01-15T20:04:51.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723570000" lon="13.188640000">
+        <ele>55.000000</ele>
+        <time>2016-01-15T20:04:56.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723668000" lon="13.188790000">
+        <ele>54.000000</ele>
+        <time>2016-01-15T20:05:01.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723767000" lon="13.188952000">
+        <ele>53.000000</ele>
+        <time>2016-01-15T20:05:06.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723852000" lon="13.189118000">
+        <ele>50.000000</ele>
+        <time>2016-01-15T20:05:11.100Z</time>
+      </trkpt>
+      <trkpt lat="55.723937000" lon="13.189303000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:05:16.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724013000" lon="13.189487000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:05:21.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724098000" lon="13.189642000">
+        <ele>49.000000</ele>
+        <time>2016-01-15T20:05:26.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724150000" lon="13.189828000">
+        <ele>48.000000</ele>
+        <time>2016-01-15T20:05:31.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724252000" lon="13.189955000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:05:36.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724352000" lon="13.190020000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:05:41.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724443000" lon="13.190040000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:05:46.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724572000" lon="13.190047000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:05:51.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724683000" lon="13.190040000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:05:56.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724803000" lon="13.190055000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:02.100Z</time>
+      </trkpt>
+      <trkpt lat="55.724937000" lon="13.190043000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:07.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725050000" lon="13.190018000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:12.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725165000" lon="13.190005000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:17.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725287000" lon="13.190038000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:22.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725395000" lon="13.190068000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:27.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725510000" lon="13.190068000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:32.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725632000" lon="13.190060000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:37.200Z</time>
+      </trkpt>
+      <trkpt lat="55.725772000" lon="13.189960000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:06:43.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725807000" lon="13.189718000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:06:48.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725792000" lon="13.189460000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:06:53.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725773000" lon="13.189227000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:06:58.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725805000" lon="13.188957000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:07:04.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725783000" lon="13.188798000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:07:09.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725740000" lon="13.188713000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:07:14.100Z</time>
+      </trkpt>
+      <trkpt lat="55.725747000" lon="13.188683000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:07:22.100Z</time>
+      </trkpt>
+    </trkseg>
+  </trk>
+  <trk>
+    <name>16-01-15_22:07:46</name>
+    <desc>GH625XT GPS tracklog data</desc>
+    <trkseg>
+      <trkpt lat="55.725747000" lon="13.188685000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:07:46Z</time>
+      </trkpt>
+      <trkpt lat="55.725785000" lon="13.188633000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:07:51Z</time>
+      </trkpt>
+      <trkpt lat="55.725880000" lon="13.188502000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:07:56Z</time>
+      </trkpt>
+      <trkpt lat="55.725978000" lon="13.188375000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:08:01.100Z</time>
+      </trkpt>
+      <trkpt lat="55.726150000" lon="13.188175000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:08:08Z</time>
+      </trkpt>
+      <trkpt lat="55.726267000" lon="13.188027000">
+        <ele>47.000000</ele>
+        <time>2016-01-15T20:08:13Z</time>
+      </trkpt>
+      <trkpt lat="55.726373000" lon="13.187897000">
+        <ele>46.000000</ele>
+        <time>2016-01-15T20:08:18Z</time>
+      </trkpt>
+      <trkpt lat="55.726477000" lon="13.187747000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:08:23Z</time>
+      </trkpt>
+      <trkpt lat="55.726577000" lon="13.187608000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:08:28Z</time>
+      </trkpt>
+      <trkpt lat="55.726705000" lon="13.187458000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:08:34Z</time>
+      </trkpt>
+      <trkpt lat="55.726803000" lon="13.187410000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:08:39Z</time>
+      </trkpt>
+      <trkpt lat="55.726920000" lon="13.187367000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:08:44.100Z</time>
+      </trkpt>
+      <trkpt lat="55.727063000" lon="13.187290000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:08:50Z</time>
+      </trkpt>
+      <trkpt lat="55.727180000" lon="13.187240000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:08:55Z</time>
+      </trkpt>
+      <trkpt lat="55.727305000" lon="13.187192000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:01Z</time>
+      </trkpt>
+      <trkpt lat="55.727417000" lon="13.187127000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:09:06Z</time>
+      </trkpt>
+      <trkpt lat="55.727520000" lon="13.187080000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:11Z</time>
+      </trkpt>
+      <trkpt lat="55.727635000" lon="13.187028000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:16Z</time>
+      </trkpt>
+      <trkpt lat="55.727770000" lon="13.186967000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:22Z</time>
+      </trkpt>
+      <trkpt lat="55.727890000" lon="13.186922000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:27Z</time>
+      </trkpt>
+      <trkpt lat="55.727995000" lon="13.186867000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:09:32Z</time>
+      </trkpt>
+      <trkpt lat="55.728100000" lon="13.186815000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:09:37.100Z</time>
+      </trkpt>
+      <trkpt lat="55.728238000" lon="13.186747000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:09:42.900Z</time>
+      </trkpt>
+      <trkpt lat="55.728365000" lon="13.186735000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:09:48Z</time>
+      </trkpt>
+      <trkpt lat="55.728497000" lon="13.186782000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:09:53Z</time>
+      </trkpt>
+      <trkpt lat="55.728638000" lon="13.186657000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:09:59Z</time>
+      </trkpt>
+      <trkpt lat="55.728747000" lon="13.186540000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:10:04Z</time>
+      </trkpt>
+      <trkpt lat="55.728865000" lon="13.186473000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:10:09.100Z</time>
+      </trkpt>
+      <trkpt lat="55.728998000" lon="13.186417000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:10:15Z</time>
+      </trkpt>
+      <trkpt lat="55.729102000" lon="13.186380000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:10:20Z</time>
+      </trkpt>
+      <trkpt lat="55.729220000" lon="13.186377000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:10:25Z</time>
+      </trkpt>
+      <trkpt lat="55.729332000" lon="13.186383000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:10:30Z</time>
+      </trkpt>
+      <trkpt lat="55.729438000" lon="13.186348000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:10:35Z</time>
+      </trkpt>
+      <trkpt lat="55.729542000" lon="13.186250000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:10:40Z</time>
+      </trkpt>
+      <trkpt lat="55.729650000" lon="13.186148000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:10:45Z</time>
+      </trkpt>
+      <trkpt lat="55.729750000" lon="13.186037000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:10:50Z</time>
+      </trkpt>
+      <trkpt lat="55.729858000" lon="13.185973000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:10:55Z</time>
+      </trkpt>
+      <trkpt lat="55.729990000" lon="13.185945000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:11:00Z</time>
+      </trkpt>
+      <trkpt lat="55.730097000" lon="13.185925000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:11:05.100Z</time>
+      </trkpt>
+      <trkpt lat="55.730232000" lon="13.185853000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:11:11Z</time>
+      </trkpt>
+      <trkpt lat="55.730357000" lon="13.185785000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:11:17Z</time>
+      </trkpt>
+      <trkpt lat="55.730453000" lon="13.185658000">
+        <ele>45.000000</ele>
+        <time>2016-01-15T20:11:22Z</time>
+      </trkpt>
+      <trkpt lat="55.730495000" lon="13.185433000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:11:27Z</time>
+      </trkpt>
+      <trkpt lat="55.730508000" lon="13.185170000">
+        <ele>44.000000</ele>
+        <time>2016-01-15T20:11:32Z</time>
+      </trkpt>
+      <trkpt lat="55.730510000" lon="13.184877000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:11:38Z</time>
+      </trkpt>
+      <trkpt lat="55.730502000" lon="13.184642000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:11:43Z</time>
+      </trkpt>
+      <trkpt lat="55.730498000" lon="13.184415000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:11:48Z</time>
+      </trkpt>
+      <trkpt lat="55.730490000" lon="13.184177000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:11:53.200Z</time>
+        <extensions>
+          <gpxtpx:TrackPointExtension>
+            <gpxtpx:hr>63</gpxtpx:hr>
+          </gpxtpx:TrackPointExtension>
+        </extensions>
+      </trkpt>
+      <trkpt lat="55.730480000" lon="13.183872000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:11:58.900Z</time>
+        <extensions>
+          <gpxtpx:TrackPointExtension>
+            <gpxtpx:hr>60</gpxtpx:hr>
+          </gpxtpx:TrackPointExtension>
+        </extensions>
+      </trkpt>
+      <trkpt lat="55.730458000" lon="13.183665000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:12:04Z</time>
+        <extensions>
+          <gpxtpx:TrackPointExtension>
+            <gpxtpx:hr>60</gpxtpx:hr>
+          </gpxtpx:TrackPointExtension>
+        </extensions>
+      </trkpt>
+      <trkpt lat="55.730410000" lon="13.183485000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:12:09Z</time>
+      </trkpt>
+      <trkpt lat="55.730365000" lon="13.183223000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:12:14Z</time>
+      </trkpt>
+      <trkpt lat="55.730318000" lon="13.183000000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:12:19Z</time>
+      </trkpt>
+      <trkpt lat="55.730277000" lon="13.182807000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:12:24Z</time>
+      </trkpt>
+      <trkpt lat="55.730232000" lon="13.182630000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:12:29Z</time>
+      </trkpt>
+      <trkpt lat="55.730182000" lon="13.182432000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:12:34Z</time>
+      </trkpt>
+      <trkpt lat="55.730137000" lon="13.182265000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:12:39Z</time>
+      </trkpt>
+      <trkpt lat="55.730095000" lon="13.182072000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:12:44Z</time>
+      </trkpt>
+      <trkpt lat="55.730030000" lon="13.181925000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:12:49Z</time>
+      </trkpt>
+      <trkpt lat="55.729985000" lon="13.181777000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:12:54Z</time>
+      </trkpt>
+      <trkpt lat="55.729975000" lon="13.181563000">
+        <ele>43.000000</ele>
+        <time>2016-01-15T20:12:59Z</time>
+      </trkpt>
+      <trkpt lat="55.729950000" lon="13.181340000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:13:04Z</time>
+      </trkpt>
+      <trkpt lat="55.729923000" lon="13.181118000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:13:09Z</time>
+      </trkpt>
+      <trkpt lat="55.729893000" lon="13.180897000">
+        <ele>40.000000</ele>
+        <time>2016-01-15T20:13:14Z</time>
+      </trkpt>
+      <trkpt lat="55.729862000" lon="13.180687000">
+        <ele>38.000000</ele>
+        <time>2016-01-15T20:13:19Z</time>
+      </trkpt>
+      <trkpt lat="55.729842000" lon="13.180490000">
+        <ele>37.000000</ele>
+        <time>2016-01-15T20:13:24Z</time>
+      </trkpt>
+      <trkpt lat="55.729810000" lon="13.180272000">
+        <ele>37.000000</ele>
+        <time>2016-01-15T20:13:29Z</time>
+      </trkpt>
+      <trkpt lat="55.729780000" lon="13.180008000">
+        <ele>37.000000</ele>
+        <time>2016-01-15T20:13:35Z</time>
+      </trkpt>
+      <trkpt lat="55.729755000" lon="13.179793000">
+        <ele>38.000000</ele>
+        <time>2016-01-15T20:13:40Z</time>
+      </trkpt>
+      <trkpt lat="55.729722000" lon="13.179608000">
+        <ele>38.000000</ele>
+        <time>2016-01-15T20:13:45Z</time>
+      </trkpt>
+      <trkpt lat="55.729693000" lon="13.179437000">
+        <ele>38.000000</ele>
+        <time>2016-01-15T20:13:50Z</time>
+      </trkpt>
+      <trkpt lat="55.729668000" lon="13.179273000">
+        <ele>39.000000</ele>
+        <time>2016-01-15T20:13:55Z</time>
+      </trkpt>
+      <trkpt lat="55.729653000" lon="13.179100000">
+        <ele>39.000000</ele>
+        <time>2016-01-15T20:14:00Z</time>
+      </trkpt>
+      <trkpt lat="55.729630000" lon="13.178922000">
+        <ele>39.000000</ele>
+        <time>2016-01-15T20:14:05Z</time>
+      </trkpt>
+      <trkpt lat="55.729608000" lon="13.178742000">
+        <ele>40.000000</ele>
+        <time>2016-01-15T20:14:10Z</time>
+      </trkpt>
+      <trkpt lat="55.729582000" lon="13.178577000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:14:15Z</time>
+      </trkpt>
+      <trkpt lat="55.729557000" lon="13.178387000">
+        <ele>41.000000</ele>
+        <time>2016-01-15T20:14:20Z</time>
+      </trkpt>
+      <trkpt lat="55.729528000" lon="13.178217000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:14:25Z</time>
+      </trkpt>
+      <trkpt lat="55.729520000" lon="13.178168000">
+        <ele>42.000000</ele>
+        <time>2016-01-15T20:14:29Z</time>
+      </trkpt>
+    </trkseg>
+  </trk>
+</gpx>
diff --git a/testo.d/globalsat_sport.test b/testo.d/globalsat_sport.test
new file mode 100644 (file)
index 0000000..a279ce5
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Basic globalsat sport tests (readonly)
+#
+# Test files generated with
+# sudo ./gpsbabel -i globalsat,dump-file=reference/track/globalsat_gh625XT.bin -f /dev/ttyUSB0 -o gpx,garminextensions -F globalsat_demo.gpx
+# sudo ./gpsbabel -i globalsat,input-is-dump-file=1 -f reference/track/globalsat_gh625XT.bin -o gpx,garminextensions -F reference/track/globalsat_gh625XT.gpx
+# 
+rm -f ${TMPDIR}/globalsat_*
+gpsbabel -i globalsat,input-is-dump-file=1,dump-file=${TMPDIR}/globalsat_gh625XT.bin -f ${REFERENCE}/track/globalsat_gh625XT.bin -o gpx,garminextensions -F ${TMPDIR}/globalsat_gh625XT.gpx
+compare ${REFERENCE}/track/globalsat_gh625XT.bin ${TMPDIR}/globalsat_gh625XT.bin
+compare ${REFERENCE}/track/globalsat_gh625XT.gpx ${TMPDIR}/globalsat_gh625XT.gpx
diff --git a/vecs.cc b/vecs.cc
index bd76f07693b8a211f8c60a52ae43a1e0d7a9e051..4098ed8641c849734860eabc668a0f00fcc650e2 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
@@ -55,6 +55,7 @@ extern ff_vecs_t gcdb_vecs;
 extern ff_vecs_t gdb_vecs;
 extern ff_vecs_t geoniche_vecs;
 extern ff_vecs_t geo_vecs;
+extern ff_vecs_t globalsat_sport_vecs;
 extern ff_vecs_t glogbook_vecs;
 extern ff_vecs_t google_vecs;
 extern ff_vecs_t google_dir_vecs;
@@ -1101,6 +1102,13 @@ vecs_t vec_list[] = {
     "ovl",
     NULL,
   },
+  {
+    &globalsat_sport_vecs,
+    "globalsat",
+    "GlobalSat GH625XT GPS training watch",
+    NULL,
+    NULL,
+  },
 #endif // MAXIMAL_ENABLED
   {
     NULL,
diff --git a/xmldoc/formats/globalsat.xml b/xmldoc/formats/globalsat.xml
new file mode 100644 (file)
index 0000000..16002e7
--- /dev/null
@@ -0,0 +1,31 @@
+<para>
+  Serial download protocol for the <productname>GlobalSat Sport gh625XT</productname> training watch.
+</para>
+<para>
+The GlobalSat Sport GPS training device present themselves as USBserial devices.
+To get the training just connect the device using the supplied
+USB cable to your computer and the device will show up as a serial device.
+</para>
+<!-- para>
+  <ulink url="http://www.globalsat.com.tw/products-page_new.php?menu=2\&gs_en_product_id=5\&gs_en_product_cnt_id=32">GlobalSat sport gh625XT</ulink>
+</para -->
+<para><userinput>gpsbabel -i globalsat -f /dev/ttyUSB0 -o gpx,garminextensions -F outfile.gpx</userinput></para>
+<example id="globalsat-showlist">
+  <title>Command showing list of tracks on device</title>
+  <para><userinput>gpsbabel -i globalsat,showlist=1 -f /dev/ttyUSB0</userinput></para>
+</example>
+<example id="globalsat-track">
+  <title>Command track can be used to fetch a single track, defult is all tracks</title>
+  <para><userinput>gpsbabel -i globalsat,track=number -f /dev/ttyUSB0 -o gpx,garminextensions -F outfile.gpx</userinput></para>
+</example>
+
+<para>
+  The gh625XT USB cable provides a physical USB interface to the host computer, but
+  internally it uses a Prolific PL-2303 chip to do this.  So you must have
+  drivers installed on your computer to recognize the PL-2303 and provide
+  that data as a serial port to software like GPSBabel. Such software
+  comes with the unit for Windows or can be downloaded.
+</para>
+
+
+